#ifndef __C_NGISMODEL_MODEL_RUNTIME_H__
#define __C_NGISMODEL_MODEL_RUNTIME_H__

#include "IModelRuntime.h"
#include <string>
#include <vector>
#include "tinyxml2.h"
using namespace tinyxml2;

namespace NGIS
{
	namespace Model
	{
		class CModelRuntime : public IModelRuntime
		{
		public:
			CModelRuntime()
			{

			}

			~CModelRuntime()
			{

			}

			bool loadFromXml(const XMLElement* pModelClassElement)
			{
				const XMLElement* runtimeEle = pModelClassElement->FirstChildElement("Runtime");
				mName = runtimeEle->Attribute("name");
				mVersion = runtimeEle->Attribute("version");
				mBaseDirectory = runtimeEle->Attribute("baseDir");
				mEntry = runtimeEle->Attribute("entry");

				const XMLElement* hardwareConfigures = runtimeEle->FirstChildElement("HardwareConfigures");
				const XMLElement* softwareConfigures = runtimeEle->FirstChildElement("SoftwareConfigures");
				const XMLElement* modelAssemblyConfigures = runtimeEle->FirstChildElement("Assemblies");
				const XMLElement* supportiveConfigures = runtimeEle->FirstChildElement("SupportiveResources");

				for (const XMLElement* childEle = hardwareConfigures->FirstChildElement(); 
					childEle; childEle = childEle->NextSiblingElement())
				{
					HardwareRequirement temp_requirement;
					temp_requirement.requirementKey = childEle->Attribute("key");
					temp_requirement.requirementValue = childEle->Attribute("value");
					mHardwareRequirementList.push_back(temp_requirement);
				}

				for (const XMLElement* childEle = softwareConfigures->FirstChildElement(); 
					childEle; childEle = childEle->NextSiblingElement())
				{
					SoftwareRequirement temp_requirement;
					temp_requirement.requirementKey = childEle->Attribute("key");
					temp_requirement.requirementValue = childEle->Attribute("value");
					mSoftwareRequirementList.push_back(temp_requirement);
				}

				for (const XMLElement* childEle = modelAssemblyConfigures->FirstChildElement(); 
					childEle; childEle = childEle->NextSiblingElement())
				{
					ModelAssembly temp_requirement;
					temp_requirement.assemblyName = childEle->Attribute("name");
					temp_requirement.assemblyPath = childEle->Attribute("path");
					mModelAssemblyList.push_back(temp_requirement);
				}

				for (const XMLElement* childEle = supportiveConfigures->FirstChildElement(); 
					childEle; childEle = childEle->NextSiblingElement())
				{
					SupportiveResource temp_requirement;
					temp_requirement.resourceType = childEle->Attribute("type");
					temp_requirement.resourceName = childEle->Attribute("name");
					mSupportiveResourceList.push_back(temp_requirement);
				}

				return true;
			}

			bool formatToXml(XMLElement* pModelClassElement)
			{
				XMLDocument* doc = pModelClassElement->GetDocument();
				XMLElement* runtimeEle = doc->NewElement("Runtime");
				runtimeEle->SetAttribute("name", mName.c_str());
				runtimeEle->SetAttribute("version", mVersion.c_str());
				runtimeEle->SetAttribute("baseDir", mBaseDirectory.c_str());
				runtimeEle->SetAttribute("entry", mEntry.c_str());

				XMLElement* hardwareConfigures = doc->NewElement("HardwareConfigures");
				XMLElement* softwareConfigures = doc->NewElement("SoftwareConfigures");
				XMLElement* modelAssemblyConfigures = doc->NewElement("Assemblies");
				XMLElement* supportiveConfigures = doc->NewElement("SupportiveResources");

				for (int i=0; i<mHardwareRequirementList.size(); i++)
				{
					XMLElement* temp_ele = doc->NewElement("Add");
					temp_ele->SetAttribute("key", mHardwareRequirementList[i].requirementKey.c_str());
					temp_ele->SetAttribute("value", mHardwareRequirementList[i].requirementValue.c_str());
					hardwareConfigures->LinkEndChild(temp_ele);
				}

				for (int i=0; i<mSoftwareRequirementList.size(); i++)
				{
					XMLElement* temp_ele = doc->NewElement("Add");
					temp_ele->SetAttribute("key", mSoftwareRequirementList[i].requirementKey.c_str());
					temp_ele->SetAttribute("value", mSoftwareRequirementList[i].requirementValue.c_str());
					softwareConfigures->LinkEndChild(temp_ele);
				}

				for (int i=0; i<mModelAssemblyList.size(); i++)
				{
					XMLElement* temp_ele = doc->NewElement("Assembly");
					temp_ele->SetAttribute("name", mModelAssemblyList[i].assemblyName.c_str());
					temp_ele->SetAttribute("path", mModelAssemblyList[i].assemblyPath.c_str());
					modelAssemblyConfigures->LinkEndChild(temp_ele);
				}

				for (int i=0; i<mSupportiveResourceList.size(); i++)
				{
					XMLElement* temp_ele = doc->NewElement("Add");
					temp_ele->SetAttribute("type", mSupportiveResourceList[i].resourceType.c_str());
					temp_ele->SetAttribute("name", mSupportiveResourceList[i].resourceName.c_str());
					supportiveConfigures->LinkEndChild(temp_ele);
				}

				runtimeEle->LinkEndChild(hardwareConfigures);
				runtimeEle->LinkEndChild(softwareConfigures);
				runtimeEle->LinkEndChild(modelAssemblyConfigures);
				runtimeEle->LinkEndChild(supportiveConfigures);

				pModelClassElement->LinkEndChild(runtimeEle);

				return true;
			}

		public:
			virtual std::string getName()
			{
				return mName;
			}

			virtual void setName(std::string pName)
			{
				mName = pName;
			}

			virtual std::string getVersion()
			{
				return mVersion;
			}

			virtual void setVersion(std::string pVersion)
			{
				mVersion = pVersion;
			}

			virtual std::string getEntry()
			{
				return mEntry;
			}

			virtual void setEntry(std::string pEntry)
			{
				mEntry = pEntry;
			}

			virtual std::string getBaseDirectory() 
			{
				return mBaseDirectory;
			}

			virtual void setBaseDirectory(std::string pDirectory) 
			{
				mBaseDirectory = pDirectory;
			}

			//////////////////////////////////////////////////////////////////////////
			virtual bool addHardwareRequirement(HardwareRequirement& pRequirement)
			{
				for (int i=0; i<mHardwareRequirementList.size(); i++)
				{
					if (mHardwareRequirementList[i].requirementKey == pRequirement.requirementKey &&
						mHardwareRequirementList[i].requirementValue == pRequirement.requirementValue)
					{
						return false;
					}
				}
				mHardwareRequirementList.push_back(pRequirement);
				return true;
			}

			virtual bool addSoftwareRequirement(SoftwareRequirement& pRequirement) 
			{
				for (int i=0; i<mSoftwareRequirementList.size(); i++)
				{
					if (mSoftwareRequirementList[i].requirementKey == pRequirement.requirementKey &&
						mSoftwareRequirementList[i].requirementValue == pRequirement.requirementValue)
					{
						return false;
					}
				}
				mSoftwareRequirementList.push_back(pRequirement);
				return true;
			}

			virtual bool addModelAssembly(ModelAssembly& pRequirement) 
			{
				for (int i=0; i<mModelAssemblyList.size(); i++)
				{
					if (mModelAssemblyList[i].assemblyName == pRequirement.assemblyName &&
						mModelAssemblyList[i].assemblyPath == pRequirement.assemblyPath)
					{
						return false;
					}
				}
				mModelAssemblyList.push_back(pRequirement);
				return true;
			}

			virtual bool addSupportiveResource(SupportiveResource& pRequirement)
			{
				for (int i=0; i<mSupportiveResourceList.size(); i++)
				{
					if (mSupportiveResourceList[i].resourceType == pRequirement.resourceType &&
						mSupportiveResourceList[i].resourceName == pRequirement.resourceName)
					{
						return false;
					}
				}
				mSupportiveResourceList.push_back(pRequirement);
				return true;
			}

			//////////////////////////////////////////////////////////////////////////
			virtual int getHardwareRequirementCount()
			{
				return mHardwareRequirementList.size();
			}

			virtual int getSoftwareRequirementCount()
			{
				return mSoftwareRequirementList.size();
			}

			virtual int getModelAssemblyCount()
			{
				return mModelAssemblyList.size();
			}

			virtual int getSupportiveResourceCount()
			{
				return mSupportiveResourceList.size();
			}

			virtual bool getHardwareRequirement(int idx, HardwareRequirement& pRequirement) 
			{
				if (idx<0 || idx>= mHardwareRequirementList.size()) return false;
				pRequirement = mHardwareRequirementList[idx];
				return true;
			}

			virtual bool getSoftwareRequirement(int idx, SoftwareRequirement& pRequirement) 
			{
				if (idx<0 || idx>= mSoftwareRequirementList.size()) return false;
				pRequirement = mSoftwareRequirementList[idx];
				return true;
			}

			virtual bool getModelAssembly(int idx, ModelAssembly& pRequirement) 
			{
				if (idx<0 || idx>= mModelAssemblyList.size()) return false;
				pRequirement = mModelAssemblyList[idx];
				return true;
			}

			virtual bool getSupportiveResource(int idx, SupportiveResource& pRequirement) 
			{
				if (idx<0 || idx>= mSupportiveResourceList.size()) return false;
				pRequirement = mSupportiveResourceList[idx];
				return true;
			}

			//////////////////////////////////////////////////////////////////////////
			virtual bool removeHardwareRequirement(HardwareRequirement& pRequirement) 
			{
				std::vector<HardwareRequirement>::iterator it = mHardwareRequirementList.begin();
				std::vector<HardwareRequirement>::iterator end = mHardwareRequirementList.end();
				for ( ; it!=end; it++)
				{
					if (it->requirementKey == pRequirement.requirementKey &&
						it->requirementValue == pRequirement.requirementValue)
					{
						mHardwareRequirementList.erase(it);
						return true;
					}
				}
				return false;
			}

			virtual bool removeSoftwareRequirement(SoftwareRequirement& pRequirement)
			{
				std::vector<SoftwareRequirement>::iterator it = mSoftwareRequirementList.begin();
				std::vector<SoftwareRequirement>::iterator end = mSoftwareRequirementList.end();
				for ( ; it!=end; it++)
				{
					if (it->requirementKey == pRequirement.requirementKey &&
						it->requirementValue == pRequirement.requirementValue)
					{
						mSoftwareRequirementList.erase(it);
						return true;
					}
				}
				return false;
			}

			virtual bool removeModelAssembly(ModelAssembly& pRequirement)
			{
				std::vector<ModelAssembly>::iterator it = mModelAssemblyList.begin();
				std::vector<ModelAssembly>::iterator end = mModelAssemblyList.end();
				for ( ; it!=end; it++)
				{
					if (it->assemblyName == pRequirement.assemblyName &&
						it->assemblyPath == pRequirement.assemblyPath)
					{
						mModelAssemblyList.erase(it);
						return true;
					}
				}
				return false;
			}

			virtual bool removeSupportiveResource(SupportiveResource& pRequirement) 
			{
				std::vector<SupportiveResource>::iterator it = mSupportiveResourceList.begin();
				std::vector<SupportiveResource>::iterator end = mSupportiveResourceList.end();
				for ( ; it!=end; it++)
				{
					if (it->resourceType == pRequirement.resourceType &&
						it->resourceName == pRequirement.resourceName)
					{
						mSupportiveResourceList.erase(it);
						return true;
					}
				}
				return false;
			}

			virtual bool removeHardwareRequirement(int idx) 
			{
				if (idx<0 || idx>= mHardwareRequirementList.size()) return false;
				std::vector<HardwareRequirement>::iterator it = mHardwareRequirementList.begin();
				mHardwareRequirementList.erase(it+idx);
				return true;
			}

			virtual bool removeSoftwareRequirement(int idx) 
			{
				if (idx<0 || idx>= mSoftwareRequirementList.size()) return false;
				std::vector<SoftwareRequirement>::iterator it = mSoftwareRequirementList.begin();
				mSoftwareRequirementList.erase(it+idx);
				return true;
			}

			virtual bool removeModelAssembly(int idx) 
			{
				if (idx<0 || idx>= mModelAssemblyList.size()) return false;
				std::vector<ModelAssembly>::iterator it = mModelAssemblyList.begin();
				mModelAssemblyList.erase(it+idx);
				return true;
			}

			virtual bool removeSupportiveResource(int idx) 
			{
				if (idx<0 || idx>= mSupportiveResourceList.size()) return false;
				std::vector<SupportiveResource>::iterator it = mSupportiveResourceList.begin();
				mSupportiveResourceList.erase(it+idx);
				return true;
			}

			virtual bool compareOther(IModelRuntime* pRuntime, std::string& obj, std::string& name)
			{
				if (mName != pRuntime->getName())
				{
					obj = "Runtime";
					name = "name";
					return false;
				}
				if (mVersion != pRuntime->getVersion())
				{
					obj = "Runtime";
					name = "version";
					return false;
				}
				if (mEntry != pRuntime->getEntry())
				{
					obj = "Runtime";
					name = "entry";
					return false;
				}
				if (mBaseDirectory != pRuntime->getBaseDirectory())
				{
					obj = "Runtime";
					name = "baseDir";
					return false;
				}

				if (mHardwareRequirementList.size() != pRuntime->getHardwareRequirementCount())
				{
					obj = "HardwareConfigures";
					name = "HardwareRequirementCount";
					return false;
				}
				for (int i=0; i<mHardwareRequirementList.size(); i++)
				{
					HardwareRequirement temp_req1 = mHardwareRequirementList[i];
					HardwareRequirement temp_req2;
					pRuntime->getHardwareRequirement(i, temp_req2);
					if (temp_req1.compareOther(temp_req2)==false)
					{
						obj = "Hardware";
						name = temp_req1.requirementKey + "<-->" + temp_req2.requirementKey;
						return false;
					}
				}
				if (mSoftwareRequirementList.size() != pRuntime->getSoftwareRequirementCount())
				{
					obj = "SoftwareConfigures";
					name = "SoftwareRequirementCount";
					return false;
				}
				for (int i=0; i<mSoftwareRequirementList.size(); i++)
				{
					SoftwareRequirement temp_req1 = mSoftwareRequirementList[i];
					SoftwareRequirement temp_req2;
					pRuntime->getSoftwareRequirement(i, temp_req2);
					if (temp_req1.compareOther(temp_req2)==false)
					{
						obj = "Software";
						name = temp_req1.requirementKey + "<-->" + temp_req2.requirementKey;
						return false;
					}
				}
				if (mModelAssemblyList.size() != pRuntime->getModelAssemblyCount())
				{
					obj = "Assemblies";
					name = "AssemblyCount";
					return false;
				}
				for (int i=0; i<mModelAssemblyList.size(); i++)
				{
					ModelAssembly temp_assembly1 = mModelAssemblyList[i];
					ModelAssembly temp_assembly2;
					pRuntime->getModelAssembly(i, temp_assembly2);
					if (temp_assembly1.compareOther(temp_assembly2)==false)
					{
						obj = "Assembly";
						name = temp_assembly1.assemblyName + "<-->" + temp_assembly2.assemblyName;
						return false;
					}
				}
				if (mSupportiveResourceList.size() != pRuntime->getSupportiveResourceCount())
				{
					obj = "SupportiveResources";
					name = "SupportiveResourceCount";
					return false;
				}
				for (int i=0; i<mSupportiveResourceList.size(); i++)
				{
					SupportiveResource temp_res1 = mSupportiveResourceList[i];
					SupportiveResource temp_res2;
					pRuntime->getSupportiveResource(i, temp_res2);
					if (temp_res1.compareOther(temp_res2)==false)
					{
						obj = "SupportiveResource";
						name = temp_res1.resourceName + "<-->" + temp_res2.resourceName;
						return false;
					}
				}
				return true;
			}

		private:
			std::string mName;
			std::string mVersion;
			std::string mEntry;
			std::string mBaseDirectory;

			std::vector<HardwareRequirement> mHardwareRequirementList;
			std::vector<SoftwareRequirement> mSoftwareRequirementList;
			std::vector<ModelAssembly> mModelAssemblyList;
			std::vector<SupportiveResource> mSupportiveResourceList;
		};
	}
}

#endif

